package com.gitee.wsl.compose.chart.geometry.model

import com.gitee.wsl.math.degreesToRadians
import kotlin.jvm.JvmStatic
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt

/*
 * Copyright 2012, 2013 Hannes Janetzek
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

object GeometryUtils {
    /**
     * Test if point x/y is in polygon defined by vertices[offset ...
     * offset+length]
     *
     *
     * -- from www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
     *
     *
     * If there is only one connected component, then it is optional to repeat
     * the first vertex at the end. It's also optional to surround the component
     * with zero vertices.
     *
     *
     * The polygon may contain multiple separate components, and/or holes,
     * provided that you separate the components and holes with a (0,0) vertex,
     * as follows.
     *  * First, include a (0,0) vertex.
     *  * Then include the first component' vertices, repeating its first
     * vertex after the last vertex.
     *  * Include another (0,0) vertex.
     *  * Include another component or hole, repeating its first vertex after
     * the last vertex.
     *  * Repeat the above two steps for each component and hole.
     *  * Include a final (0,0) vertex.
     *
     *
     * Each component or hole's vertices may be listed either clockwise or
     * counter-clockwise.
     */
    fun pointInPoly(
        x: Float, y: Float, vertices: FloatArray,
        length: Int, offset: Int
    ): Boolean {
        val end = offset + length
        var inside = false
        var i = offset
        var j = end - 2
        while (i < end) {
            if (vertices[i + 1] > y != vertices[j + 1] > y &&
                (x < (vertices[j] - vertices[i]) * (y - vertices[i + 1])
                        / (vertices[j + 1] - vertices[i + 1]) + vertices[i])
            ) inside = !inside
            j = i
            i += 2
        }
        return inside
    }

    fun area(ax: Float, ay: Float, bx: Float, by: Float, cx: Float, cy: Float): Float {
        val area = ((ax - cx) * (by - cy)
                - (bx - cx) * (ay - cy))
        return (if (area < 0) -area else area) * 0.5f
    }

    fun area(a: FloatArray, p1: Int, p2: Int, p3: Int): Float {
        val area = ((a[p1] - a[p3]) * (a[p2 + 1] - a[p3 + 1])
                - (a[p2] - a[p3]) * (a[p1 + 1] - a[p3 + 1]))
        return (if (area < 0) -area else area) * 0.5f
    }

    fun squaredDistance(p: FloatArray, a: Int, b: Int): Float {
        return (p[a] - p[b]) * (p[a] - p[b]) + (p[a + 1] - p[b + 1]) * (p[a + 1] - p[b + 1])
    }

    /** square distance from a point a to a segment b,c  */ // modified from https://github.com/ekeneijeoma/simplify-java
    fun squareSegmentDistance(p: FloatArray, a: Int, b: Int, c: Int): Float {
        var x = p[b]
        var y = p[b + 1]
        var dx = p[c] - x
        var dy = p[c + 1] - y
        if (dx != 0f || dy != 0f) {
            val t = ((p[a] - x) * dx + (p[a + 1] - y) * dy) / (dx * dx + dy * dy)
            if (t > 1) {
                x = p[c]
                y = p[c + 1]
            } else if (t > 0) {
                x += dx * t
                y += dy * t
            }
        }
        dx = p[a] - x
        dy = p[a + 1] - y
        return dx * dx + dy * dy
    }

    fun distance(p: FloatArray, a: Int, b: Int): Double {
        val dx = p[a] - p[b]
        val dy = p[a + 1] - p[b + 1]
        return sqrt((dx * dx + dy * dy).toDouble())
    }

    fun dotProduct(p: FloatArray, a: Int, b: Int, c: Int): Double {
        val ux = (p[b] - p[a]).toDouble()
        val uy = (p[b + 1] - p[a + 1]).toDouble()
        val ab = sqrt(ux * ux + uy * uy)
        val vx = (p[b] - p[c]).toDouble()
        val vy = (p[b + 1] - p[c + 1]).toDouble()
        val bc = sqrt(vx * vx + vy * vy)
        val d = ab * bc
        if (d <= 0) return 0.0
        var dotp = (ux * -vx + uy * -vy) / d
        if (dotp > 1) dotp = 1.0 else if (dotp < -1) dotp = -1.0
        return dotp
    }

    @JvmStatic
    fun main(args: Array<String>) {
        val p = floatArrayOf(-1f, 0f, 0f, 0f, 0f, 0f)
        for (i in 0..8) {
            p[4] = cos((i * 45).degreesToRadians()).toFloat()
            p[5] = sin((i * 45).degreesToRadians()).toFloat()
            println(
                """> ${i * 45} ${p[3]}:${p[4]}=${dotProduct(p, 0, 2, 4)}"""
            )
        }
    }
}

